CONTENTS | INDEX | PREV | NEXT
FUNCTION
Make Utility
SYNOPSIS
dmake [file]
DESCRIPTION
Make utilities automate complex compiles. Makefiles can be
considered recipes for complex programs. Makefiles contain
"dependencies," which are rules that say things like "if this header
file changes, recompile this C file."
The idea with DMake is to provide a powerful make utility through
general features rather than specialized hacks. DMake is governed by
a few simple rules that can be combined into incredibly powerful
operations.
Generally you simply run DMake and have a list of dependencies in
your DMakefile which DMake then executes. The DMakefile may contain
three different kinds of lines:
1) COMMENTS -- Any line beginning with a '#' is a comment and ignored
# This DMakefile generates an executable for fubar
# The compiler options are as follows ...
1) ASSIGNMENTS -- Any line of the form SYMBOL = ... is considered an
assignment. Any variable references from within the assignment
will be resolved immediately.
CFLAGS= -r SRCS= x.c y.c z.c
1) DEPENDENCIES: -- A line containing a list of symbols, a colon, and
more symbols is assumed to be a dependency. Note that you cannot
have a raw filename with a colon in it as that confuses DMake.
Instead, use an ASSIGNMENT variable.
Following the dependency line is zero or more command lines --
commands to run to resolve the dependency, terminated with a blank
line.
|| NOTE: Not only is a zero-command dependency allowed, it is
|| sometimes necessary.
A particular destination may have only ONE command list so if you
have something like
a.o : a.c
with a command list to compile the source into an object you can also
have another dependency such as 'a.o : defs.h' which would NOT have
any associated command list.
dst1 ... dstN : src1 ... srcN command1 command2 ...
dst1 ... dstN : src1 ... srcN command1 command2 ...
Finally, note that a dst* or src* symbol does not need to be a
filename. It is perfectly valid to make up dummy names which are
then used as the lhs of a dependency that collects other dependencies
together.
DEPENDENCIES
When declaring dependencies you may use four different forms. The
first form is to have a single destination and several sources. This
is interpreted to mean that ALL the sources must be resolved before
the single destination can be resolved via the command list for the
dependency. The special variable, %(left), is set to the dst symbol
and the special variable %(right) is set to ALL of the src symbols
For example, this form would be used to indicate that an executable
depends on all the objects being resolved before you can run the
link.
dst : src1 src2 src3 ... srcN
The second form is the most useful in that it allows you to specify
multiple 1 : 1 dependencies. Thus, you can specify, for example,
that each object file depends on its source file counterpart for ALL
the files in your project on a single line and have a single command
list representing what to do (to compile a source file into an
object, say).
In this case %(left) and %(right) are set to each dst* : src* pair
individually and the command list is run for any individual pair that
is out of date.
dst1 dst2 dst3 ... dstN : src1 src2 src3 ... srcN
The next form may be used to specify that many files depend on one
file being resolved. An example of usage would be to make all the
object files depend on one header file. The command list, if any, is
run for each dst* : src pair with %(left) set to the current dst* and
%(right) set to the single source.
dst1 dst2 dst3 ... dstN : src
The last form is esoteric but sometimes useful. EACH dst* on the
left hand side depends on the entire right hand side. You can have
an arbitrary number of symbols on either side. %(left) will be set
to a particular DST while %(right) will be set to all of the SRCs.
for example, you could specify $(OBJS) :: $(HDRS) -- make all objects
depend on all headers causing a recompile to occur if any header is
modified.
dst1 dst2 dst3 ... dstN :: src1 src2 ... srcI
WILDCARDS
DMake's most powerful feature is an easy to use file list replacement
through options in a variable specification. You may insert the
contents of any variable using the form:
$(SYMBOL)
Furthermore, you can make modifications to the contents of the
variable on the fly using:
$(SYMBOL:wildcard)
only those files which match wildcard
$(SYMBOL:wildcard:wildcard)
matching files and also do a conversion
Simple */? wildcarding is used. A wildcard may contain a colon or
other punctuation but if it does you MUST surround it with quotes.
Here is a quick example:
SRCS= a.c b.c c.c d.c xx.a
OBJS= $(SRCS:*.c:"dtmp:%1.o")
all: echo $(OBJS)
Will Produce
dtmp:a.o dtmp:b.o dtmp:c.o dtmp:d.o
The first wildcard specification restricts which files from the list
are to be taken -- 'xx.a' was ignored, as you can see. Each '*' or
'?' in the first wildcard specification corresponds to %N
specifications in the second wildcard specification. You can
prepend, insert, or append text and freely mix or ignore items
matched to create your destination file list.
This capability allows you to specify your source files EXACTLY ONCE
in the DMakefile and then use the file munging capability to convert
them to the object file list, etc...
You can embed variables within variables as with the following
example (note that this time xx.a is included):
OD= dtmp:fubar/
SRCS= a.c b.c c.c d.c xx.a
OBJS= $(SRCS:*.?:"$(OD)%1.o")
all: echo $(OBJS)
Will produce
dtmp:fubar/a.o dtmp:fubar/b.o dtmp:fubar/c.o
dtmp:fubar/d.o dtmp:fubar/xx.o
As a side note, you may also specify '?' and '*' in the destination
wildcard. These are considered dummies and are equivalent to %N
where N is incremented from 1..9 for each '?' or '*' encountered.
You can use the capability anywhere in the DMakefile. Another common
thing to do is restrict your link line to include only the object
files and skip the headers:
$(EXE) : $(PROTOS) $(OBJS) $(HDRS)
dcc %(right:*.o) -o %(left)
ENVIRONMENT VARIABLES
2.0 local variables and 1.3/2.0 ENV: variables are fully accessible.
Under 2.0 you can also modify local variables on the fly.
DMake-specific variables override 2.0 local variables override ENV:
variables.
Under 2.0, any command containing <, >, `, or |, or is an alias, will
be run with System(). Thus, such commands may not be used to modify
local variables or the local environment. Also, such commands cannot
be ^C'd due to the way AmigaDOS works.
EXAMPLE
The following is an example dmakefile. The variable $(FILES) is set
to "main input output". The next two variables are constructed from
$(FILES). The command $(FILES:*:"*.c") tells dmake to take $(FILES),
look up each item ":*", and append two characters ":*.c". The
results of the conversions are listed above. This unique feature of
dmake makes for very elegant DMakeFiles.
The first rule is the default rule, executed if you just type dmake.
The rule, "sample:", states that the resulting program, "sample", is
made up from the files listed in $(FILE_OBJECTS). If the date on
"sample" is older than any dates in $(FILE_OBJECTS), the rule will
execute.
In turn, the next rule states that $(FILE_OBJECTS) ("main.o input.o
output.o") are made from $(FILE_SOURCES) ("main.c intput.c
output.c"). If any of the .o files are older than the corresponding
.c file, the rule executes.
In short, the makefile is a description of how source files inter
depend. When any file changes, dmake figures the minimum number of
steps to regenerate the final result. If you change just "input.c",
only "input.c" will recompile.
The last rule, "clean:", has no dependencies (nothing on the right
side of the :). When executed, this rule deletes all the
compiler-generated files, but not the source code. To execute this
rule, type "dmake clean". Any number of rules may exist in a single
DMakeFile.
: DMakeFile - generic
------------------------------+-------------------------------------
FILES = main input output : Equals "main input output" Set to
FILE_SOURCES = : "main.c input.c output.c" Set to
$(FILES:*:*.c") FILE_OBJECTS : "t:main.o t:input.o t:output.o"
= $(FILES:*:"t:*.o) :
------------------------------+-------------------------------------
sample: $(FILE_OBJECTS) dcc : Rule: sample is made from
$(FILE_OBJECTS) -o sample : FILE_OBJECTS (defined above as
: "t:main.o t:input.o t:output.o")
------------------------------+-------------------------------------
$(FILE_OBJECTS) : : Rule: FILE_OBJECTS are made from
$(FILE_SOURCES) dcc -c : FILE_SOURCES (see above).
%(right) -o %(left) :
------------------------------+-------------------------------------
clean: delete : Rule: "dmake clean" executes this
$(FILES) $(FILE_OBJECTS) : delete command.
------------------------------+-------------------------------------
LINE CONTINUATION AND ESCAPES
Any line may be continued by terminating it with a backslash ''. It
is possible to escape the special characters '$' and '%' by doubling
them though this is only necessary if an open-parenthesis follows the
'$' or '%' and you do not want it interpreted as a variable.
It is possible to escape ':' and other special characters by
assigning them (or a string containing them) to a variable
COMMAND SHELL
Under 2.0 commands that do not contain any sort of redirection are
run with RunCommand(). If a command is an alias or there is some
sort of redirection in the arguments it will be run with System().
Under 1.3 everything is run with Execute()
ADVANCED CAPABILITIES
Now, you may have noted earlier that I said you could not have any
given left-hand-side with more then one command list. Take, for
example:
a.o : a.c dcc %(right) -o %(left)
a.o : defs.h <--- illegal to put command list here
Actually, it isn't illegal. When DMake encounters a dependency
without a command list it will automatically 'force' the next higher
level dependency of the same left-hand-side. Therefore if you do not
have a command list for the lower level left-hand-side things work as
you expect. Note that this requires all such null dependencies to
exist AFTER the one that has the command list.
If you do have two or more command lists for the same left-hand-side
they will run independent of each other according to their individual
right hand sides. If several command lists apply then their order of
execution will be bottom-up
T FOR EXISTENCE
ther advanced feature quite useful in fully automating the
pilation process is the ability to create a directory tree on the
. That is, if you have a projects called 'fubar' and want the
ects to go into the directory DTMP:fubar/ you might want to have a
endency that creates DTMP:fubar if it does not already exist.
dtmp:fubar
X) : $(XX) makedir %(left)